package com.ficus.road.maintain.handler.monitor;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ficus.road.maintain.core.model.AlertDTO;
import com.ficus.road.maintain.core.model.Car;
import com.ficus.road.maintain.core.model.CarTrip;
import com.ficus.road.maintain.core.model.CarVideoRecord;
import com.ficus.road.maintain.dao.AlertDao;
import com.ficus.road.maintain.dao.CarTripDao;
import com.ficus.road.maintain.dao.CarVideoRecordDao;
import com.ficus.road.maintain.dto.Checklist;
import com.ficus.road.maintain.dto.ChecklistOfCar;
import com.ficus.road.maintain.util.FormatUtil;
import com.ficus.road.maintain.util.HttpClientUtil;
import com.ficus.road.maintain.util.MyDateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author xiawen yang
 * @date 2022/5/23 下午5:51
 */
@Component
@Order(1)
@Slf4j
@RequiredArgsConstructor
public class CarMonitorHandler extends AbstractMonitorHandler {

    private static String authStr;
    private final CarTripDao carTripDao;
    private final CarVideoRecordDao carVideoRecordDao;
    private final AlertDao alertDao;
    @Value("${monitor.username}")
    private String username;
    @Value("${monitor.password}")
    private String password;
    @Value("${monitor.url}")
    private String url;

    @PostConstruct
    void init() {
        authStr = getAuthorization(username, password);
    }

    @Override
    Checklist doFilter(Checklist fetch) {
        log.info("车辆监测统计开始");
        long start = System.currentTimeMillis();
        Map<String, String> headers = new HashMap<>();
        headers.put("Authorization", authStr);
        Map<String, String> params = new HashMap<>();

        Date date = new Date();
        List<ChecklistOfCar> carCheckList = fetch.getCarList();

        //出车统计
        Date startOfDay = MyDateUtil.getStartOfDay(date);
        Criteria criteria = Criteria.where("start_time").gte(startOfDay.getTime());
        Query query = new Query(Criteria.where("date").is(MyDateUtil.dateFormatStr(date, "yyyyMMdd")));
        List<CarVideoRecord> carVideoRecords = carVideoRecordDao.findByQuery(query);
        Map<String, List<CarVideoRecord>> carVideoMap = carVideoRecords.stream().collect(Collectors.groupingBy(CarVideoRecord::getCarId));

        carCheckList.stream().forEach(checklistOfCar -> {
            //统计sdk出车时长、sdk端出车状态
            calculateSDKStatus(checklistOfCar, headers, params);
            //统计硬件状态
            calculateHardwareStatus(checklistOfCar);
            //查询数据盘容量
            calculateHardwareCap(checklistOfCar, headers, params);
            //出车统计
            List<CarVideoRecord> carVideoList = carVideoMap.get(checklistOfCar.getCarId());
            if (ObjectUtils.isNotEmpty(carVideoList)) {
                Long endTime = Long.MIN_VALUE;
                Long startTime = Long.MAX_VALUE;
                //云端出车(1出车 0未出车)
                checklistOfCar.setCloudCarStatus(1);
                int cloudCarTotalMinute = 0;
                //出车时间
                for (CarVideoRecord carVideoRecord : carVideoList) {
                    endTime = Math.max(endTime, carVideoRecord.getEndTime());
                    startTime = Math.min(startTime, carVideoRecord.getStartTime());
                    cloudCarTotalMinute += carVideoRecord.getEndTime() - carVideoRecord.getStartTime();
                }
                checklistOfCar.setStartTime(MyDateUtil.dateFormatStr(new Date(startTime), "HH:mm:ss"));
                checklistOfCar.setEndTime(MyDateUtil.dateFormatStr(new Date(endTime), "HH:mm:ss"));
                //出车总时长
                checklistOfCar.setCloudCarTotalMinute(cloudCarTotalMinute / 60000);
            }
            //出车状态统计, 1故障 0未故障
            checklistOfCar.setIsCarOutStatusError(checklistOfCar.getSdkCarStatus() ^ checklistOfCar.getCloudCarStatus());
            Car car = carMap.get(checklistOfCar.getCarId());
            //版本号
            checklistOfCar.setVersion(car.getVersion());
            //里程
            Query carTripQuery = new Query(Criteria.where("car_id").is(checklistOfCar.getCarId()).and("timestamp").gte(startOfDay.getTime()));
            List<CarTrip> carTripList = carTripDao.findByQuery(carTripQuery);
            double totalMiles = carTripList.stream().mapToDouble(CarTrip::getMile).sum();
            checklistOfCar.setMiles(FormatUtil.keepDecimalsOfK(totalMiles, 2));
        });
        checklistDao.saveOrUpdate(fetch);
        log.info("车辆监测统计耗时： {} ms", System.currentTimeMillis() - start);
        return fetch;
    }

    public String getAuthorization(String username, String token) {
        //使用base64进行加密
        byte[] tokenByte = Base64.getEncoder().encode((username + ":" + token).getBytes());
        //将加密的信息转换为string
        String tokenStr = new String(tokenByte);
        return "Basic " + tokenStr;
    }

    /**
     * sdk端出车统计
     *
     * @param checklistOfCar
     */
    private void calculateSDKStatus(ChecklistOfCar checklistOfCar, Map<String, String> headers, Map<String, String> params) {
        String queryStr = "up{instance='" + checklistOfCar.getCarId() + "', job='car_status', type='cadvisor'}[1d]";
        params.put("query", queryStr);
        HttpClientUtil.HttpClientResult clientResult = HttpClientUtil.doGet(url, headers, params);
        if (clientResult.getCode() == HttpStatus.SC_OK) {
            JSONObject responseData = JSONObject.parseObject(clientResult.getContent()).getJSONObject("data");
            JSONArray resultArray = responseData.getJSONArray("result");
            if (ObjectUtils.isNotEmpty(resultArray)) {
                int upMinute = 0;
                JSONObject result = resultArray.getJSONObject(0);
                JSONArray values = result.getJSONArray("values");
                for (int i = 0; i < values.size(); i++) {
                    JSONArray value = (JSONArray) values.get(i);
                    String status = (String) value.get(1);
                    if (status.equals("1")) {
                        upMinute++;
                    }
                }
                if (upMinute > 0) {
                    // 更新出车状态：1出车 0未出车
                    checklistOfCar.setSdkCarStatus(1);
                }
                // 更新出车时长
                checklistOfCar.setSdkCarTotalMinute(upMinute);
            }
        }
    }


    /**
     * 硬件状态统计
     *
     * @param checklistOfCar
     */
    private void calculateHardwareStatus(ChecklistOfCar checklistOfCar) {
        String dateStr = MyDateUtil.dateFormatStr(new Date(), "yyyy-MM-dd");
        List<AlertDTO> alertDTOS = alertDao.findByQuery(new Query(Criteria.where("car_id").is(checklistOfCar.getCarId()).and("date").is(dateStr)));
        if (ObjectUtils.isNotEmpty(alertDTOS)) {
            alertDTOS.stream().forEach(alertDTO -> {
                switch (alertDTO.getAlertname()) {
                    case GNSSError:
                        checklistOfCar.setIsGNSSError(1);
                        checklistOfCar.setGNSSErrorTimes(alertDTO.getTimes());
                        break;
                    case LidarError:
                        checklistOfCar.setIsLidarError(1);
                        checklistOfCar.setLidarErrorTimes(alertDTO.getTimes());
                        break;
                    case CameraError:
                        checklistOfCar.setIsCameraError(1);
                        checklistOfCar.setCameraErrorTimes(alertDTO.getTimes());
                        break;
                }
            });
        }
    }

    //todo: 车保养后且第二天没出车，那么报表显示的数据是保养前的最后一个有效数据
    private void calculateHardwareCap(ChecklistOfCar checklistOfCar, Map<String, String> headers, Map<String, String> params) {
        String carId = checklistOfCar.getCarId();
        for (int day = 1; day <= 30; day++) {
            String queryParam = "node_filesystem_avail_bytes{job=\"car_status\",mountpoint=\"/mnt/storage\", instance=\'" + carId + "\'}[" + day + "d]";
            params.put("query", queryParam);
            HttpClientUtil.HttpClientResult clientResult = HttpClientUtil.doGet(url, headers, params);
            if (clientResult.getCode() == HttpStatus.SC_OK) {
                JSONObject responseData = JSONObject.parseObject(clientResult.getContent()).getJSONObject("data");
                JSONArray resultArray = responseData.getJSONArray("result");
                if (ObjectUtils.isNotEmpty(resultArray)) {
                    JSONObject result = resultArray.getJSONObject(0);
                    JSONArray values = result.getJSONArray("values");
                    JSONArray value = values.getJSONArray(values.size() - 1);
                    String capacityStr = value.getString(1);
                    Double capacity = Double.parseDouble(capacityStr);
                    capacity = capacity / (1024 * 1024 * 1024);
                    Double capG = FormatUtil.keepDecimalsOfK(capacity, 2);
                    checklistOfCar.setStorageDiskCap(capG);
                    break;
                } else if (!checklistOfCar.getStorageDiskCap().equals(0d)) {
                    // 查不到历史数据但是数据库中数据不为0，直接跳出循环
                    break;
                }
                // 查不到历史数据，数据库中数据为0，则扩大查找范围，继续查询历史数据
            }
        }
    }
}
